package dataTranser

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"
)

type HttpTaos struct {
	IP    string
	Port  int
	User  string
	Pwd   string
	token string
}

func (h *HttpTaos) Init(ip string, port int, user string, pwd string) error {

	h.IP = ip
	h.Port = port
	h.User = user
	h.Pwd = pwd
	h.token = base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", h.User, h.Pwd)))

	return h.Send([]byte("show databases;"))
}

func (h *HttpTaos) Send(data []byte, varargs ...string) error {

	if data == nil {
		return nil
	}

	req, err := http.NewRequest("POST", fmt.Sprintf("%s:%d/rest/sql/", h.IP, h.Port), bytes.NewBuffer(data))
	if err != nil {
		return fmt.Errorf("send-> %w", err)
	}

	req.Header.Set("Authorization", "Basic "+h.token)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return fmt.Errorf("send-> %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("send-> %w", err)
	}

	if resp.StatusCode == 200 {
		var jsonData map[string]any
		err := json.Unmarshal(body, &jsonData)
		if err != nil {
			return fmt.Errorf("send:unmarshal-> %w", err)
		}

		if code, ok := jsonData["code"].(float64); !ok || code != 0 {
			return fmt.Errorf("send-> code error, body %s", string(body))
		}
		return nil
	} else {
		return fmt.Errorf("send-> status code %d, body %s", resp.StatusCode, string(body))
	}
}

func (h *HttpTaos) SaveHart(gatewayId string, projectid string, rtuBreak string) error {
	return h.Send([]byte(
		fmt.Sprintf(
			"INSERT INTO iot10days.hart_%s_%s using iot10days.hart TAGS('%s','%s','%s') VALUES (now, %d,'%s');",
			gatewayId,
			projectid,
			gatewayId,
			"",
			projectid,
			time.Now().Unix(),
			rtuBreak)))
}

func (h *HttpTaos) SaveEvent(gatewayId string, tag2 string, projectId string, eventCode int, eventSrc string, eventDesc string) error {
	return h.Send([]byte(
		fmt.Sprintf(
			"INSERT INTO iot10days.events_%s_%s_%s using iot10days.events TAGS('%s','%s','%s') VALUES (now, %d,%d,'%s','%s');",
			gatewayId,
			tag2,
			projectId,
			gatewayId,
			tag2,
			projectId,
			time.Now().Unix(),
			eventCode,
			eventSrc,
			eventDesc,
		)))
}

func (h *HttpTaos) SaveData(tag1 string, gatewayId string, projectId string, val float64) error {
	return h.Send([]byte(
		fmt.Sprintf(
			"INSERT INTO iot10days.lastvalue_%s_%s_%s using iot10days.lastvalue TAGS('%s','%s','%s') VALUES (now, %d,%f);",
			tag1,
			gatewayId,
			projectId,
			tag1,
			gatewayId,
			projectId,
			time.Now().Unix(),
			val,
		)))
}

/*
func (h *HttpTaos) SendEvent(eventList []config.EventInfo, info config.DevInfo, cmyType string, pointList map[string]dataProcess.ValueInfo, transDesc string) error {
	if len(eventList) == 0 {
		return nil
	}

	err := error(nil)
	count := 0
	sqlList := make([]string, 0)
	for index, event := range eventList {
		eventtime := event.Time.Unix()
		code := event.EventEnum
		src := event.Name
		eventdesc := event.Desc
		deviceid := info.Deviceid
		proc := h.procName
		prj := info.Projectid

		logger.Debug("save taos event: code=%d, src=%s, desc=%s", code, src, eventdesc)

		sql := fmt.Sprintf(`%s_%s_%s_%s USING %s TAGS ('%s', '%s', '%s') VALUES (now, %d, %d, '%s', '%s')`,
			h.taosConfig.EventTable, deviceid, proc, prj,
			h.taosConfig.EventTable,
			deviceid, proc, prj,
			eventtime, code, src, eventdesc)
		sqlList = append(sqlList, sql)
		count++

		if count == 100 || index == len(eventList)-1 {
			err = h.Send(h.sqlBuild(sqlList))
			count = 0
		}
	}
	return err
}

func (h *HttpTaos) SendData(dataList []dataProcess.UploadValueInfo, info config.DevInfo, cmyType string) error {
	if len(dataList) == 0 {
		return nil
	}

	err := error(nil)
	count := 0
	sqlList := make([]string, 0)
	for index, data := range dataList {
		dataTime := time.Now().Unix()
		val := data.LastValue
		guid := data.Name
		deviceid := info.Deviceid
		prj := info.Projectid

		if data.PointType == "yc" {

			if !dataProcess.RtuErrorMap[data.ByDevice].IsBreak {
				sql := fmt.Sprintf(`%s_%s_%s_%s USING %s TAGS ('%s', '%s', '%s') VALUES (now, %d, %s)`,
					h.taosConfig.DataTable, guid, deviceid, prj,
					h.taosConfig.DataTable, guid, deviceid, prj,
					dataTime, strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.3f", val), "0"), "."))
				sqlList = append(sqlList, sql)
			}
		} else {
			if !dataProcess.RtuErrorMap[data.ByDevice].IsBreak ||
				strings.Compare(data.Name, fmt.Sprintf("%s%s", data.ByDevice, dataProcess.RTU_BREAK_MASK)) == 0 {
				sql := fmt.Sprintf(`%s_%s_%s_%s USING %s TAGS ('%s', '%s', '%s') VALUES (now, %d, %d)`,
					h.taosConfig.DataTable, guid, deviceid, prj,
					h.taosConfig.DataTable, guid, deviceid, prj,
					dataTime, int(val))
				sqlList = append(sqlList, sql)
			}
		}

		count++

		if count == 100 || index == len(dataList)-1 {
			err = h.Send(h.sqlBuild(sqlList))
			count = 0
		}
	}
	return err
}

func (h *HttpTaos) SendStatsData(dataList map[string]dataProcess.ValueInfo, t time.Time, info config.DevInfo) (retErr error) {

	defer func() {
		if unknownErr := recover(); unknownErr != nil {
			retErr = fmt.Errorf("taos SendStatsData panic : [%v]", unknownErr)
		}
	}()

	if len(dataList) == 0 {
		return nil
	}

	err := error(nil)
	count := 0
	sqlList := make([]string, 0)
	for _, data := range dataList {

		if !data.Config.MaxEnable && !data.Config.MinEnable && !data.Config.AvgEnable &&
			!data.Config.DiffEnable && !data.Config.VarEnable {
			continue
		}

		guid := data.Config.Name
		deviceid := info.Deviceid
		prj := info.Projectid
		formattedTime := t.Format("2006-01-02 15:04:00.000")
		st, _ := time.Parse("2006-01-02 15:04:00.000", formattedTime)

		unixTime := st.Unix()

		sql := fmt.Sprintf(`%s_%s_%s_%s USING %s TAGS ('%s', '%s', '%s') VALUES (now, %d, %s, %s, %s, %s, %s)`,
			h.taosConfig.StatsTable, guid, deviceid, prj,
			h.taosConfig.StatsTable, guid, deviceid, prj,
			unixTime, h.getStatsValue(data, "max"), h.getStatsValue(data, "min"),
			h.getStatsValue(data, "avg"), h.getStatsValue(data, "diff"), h.getStatsValue(data, "var"))
		sqlList = append(sqlList, sql)
		count++

		if count == 100 {
			err = h.Send(h.sqlBuild(sqlList))
			sqlList = make([]string, 0)
			count = 0
		}
	}
	if count > 0 {
		err = h.Send(h.sqlBuild(sqlList))
	}
	return err
}

func (h *HttpTaos) SendPing(info config.DevInfo, pingHealth5min config.PingHealth) error {
	deviceid := info.Deviceid
	prj := info.Projectid
	sql := fmt.Sprintf(`%s_%s_%s_%s USING %s TAGS ('%s', '%s', '%s') VALUES (now,%d, %d, %d)`,
		h.taosConfig.PingTable, info.Guid, deviceid, prj,
		h.taosConfig.PingTable, info.Guid, deviceid, prj,
		time.Now().Unix(), pingHealth5min.WarnCount, pingHealth5min.ErrorCount)
	return h.Send(h.sqlBuild([]string{sql}))

}

func (h *HttpTaos) sqlBuild(eventList []string) []byte {
	insertSql := "INSERT INTO "
	for index, sql := range eventList {
		buffer := ""
		//taos数据库ts相同无法插入，将now改为毫秒递增的时间戳
		buffer += strings.Replace(sql, "(now", fmt.Sprintf("(now+%da", int64(index)), 1)
		// [strings.Index(sql, "VALUES")+7:]
		insertSql += buffer
		insertSql += "  "
	}
	logger.Debug("%s", insertSql)
	return []byte(insertSql)
}

func (h *HttpTaos) getStatsValue(data dataProcess.ValueInfo, statsType string) string {
	switch statsType {
	case "max":
		if data.Config.MaxEnable {
			return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.3f", data.StatsData.Max), "0"), ".")
		} else {
			return "null"
		}
	case "min":
		if data.Config.MinEnable {
			return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.3f", data.StatsData.Min), "0"), ".")
		} else {
			return "null"
		}
	case "avg":
		if data.Config.AvgEnable {
			return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.3f", data.StatsData.Avg), "0"), ".")
		} else {
			return "null"
		}
	case "var":
		if data.Config.VarEnable {
			return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.3f", data.StatsData.Variance), "0"), ".")
		} else {
			return "null"
		}
	case "diff":
		if data.Config.DiffEnable {
			return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.3f", data.StatsData.Diff), "0"), ".")
		} else {
			return "null"
		}
	}
	return "null"
}*/
